/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "Shared/Net/Socket.h"
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <io.h>
#include "Shared/Net/SocketEventHandler.h"


namespace Shared {
namespace Net {

    bool Socket::open(struct event_base *evBase, evutil_socket_t fd, 
    struct sockaddr *sa /* = NULL */, int sockLen /* = 0 */)
    {
        SHARED_ASSERT(evBase && fd != -1);
        _fd = fd;

        do
        {
            {
                struct sockaddr_storage ss;
                ev_socklen_t socklen = sizeof(ss);
                char addrbuf[128];
                void *inaddr;
                memset(&ss, 0, sizeof(ss));
                if (getpeername(fd, (struct sockaddr *)&ss, &socklen)) {
                    break;
                }
                if (ss.ss_family == AF_INET) {
                    _port = ntohs(((struct sockaddr_in*)&ss)->sin_port);
                    inaddr = &((struct sockaddr_in*)&ss)->sin_addr;
                }
                else if (ss.ss_family == AF_INET6) {
                    _port = ntohs(((struct sockaddr_in6*)&ss)->sin6_port);
                    inaddr = &((struct sockaddr_in6*)&ss)->sin6_addr;
                }
                else {
                    break;
                }
                _ip = evutil_inet_ntop(ss.ss_family, inaddr, addrbuf,
                    sizeof(addrbuf));
            }

            _bev = bufferevent_socket_new(evBase, fd, BEV_OPT_CLOSE_ON_FREE);
            if (!_bev) {
                break;
            }

            bufferevent_setcb(_bev,
                &Socket::readCallback, 
                &Socket::writeCallback, 
                &Socket::eventCallback, 
                this);
            bufferevent_disable(_bev, EV_WRITE);
            bufferevent_enable(_bev, EV_READ);

            return true;
        } while (false);

        return false;
    }

    void Socket::shutdownRead(void)
    {
        ::shutdown(_fd, SD_RECEIVE);
    }

    void Socket::shutdownWrite(void)
    {
        ::shutdown(_fd, SD_SEND);
    }

}}